// NOTE: 应当确保识别的结果总是"仅仅"返回true/false

// JS中的概念
type Nullish = null | undefined
type Primitive = string | number | bigint | boolean | symbol | Nullish
type Falsy = false | "" | 0 | Nullish

// TS中的概念
// LiteralType 字面类型
type LiteralType = NonNullable<Primitive> // string | number | bigint | boolean | symbol
// 
type Numeric = bigint | number

// 守护函数
function isNullish(x: any): x is Nullish {
    return x === null || x === undefined
}

type IsNullish = typeof isNullish
let f: IsNullish = isNullish

// 声明其他守护函数类型(用于预定义一些守护函数, 包括谓词签名与断言签名)
// 是为了给JavaScript类型守护函数的类型标注做的
type Is<T> = (x: any) => x is T;
type IsTrue = Is<true>

type Is2 = <T>(x: any) => x is T
type FromIs2<T> = ReturnType<(a: Is2) => typeof a<T>>
type IsFalse = FromIs2<false>

// =============================================================================
// - 单类型/基础类型检测
type IsAny<T> = 0 extends 1 & T ? true : false // never, 1, any
type IsNotAny<T> = true extends IsAny<T> ? false: true

// NOTE: 特例(any作为左侧, 且右侧不是any/unknown时, 会触发分布式条件判断)
//  - any不是裸类型参数，但上述情况下会采用分布式条件类型的逻辑
type IsNever<T> = [T] extends [never] ? true : false
type a = IsNever<never> // true
type b = IsNever<false> // false
type IsNotNever<T> = true extends IsNever<T> ? false : true

// true/false (即使类型限制为 boolean， 但还是能传入never, any, boolean)
// 所以一定要使用字面 的 true/false 作为 extends 左侧的值，才能保证 识别的结果总是"仅仅"返回true/false
type Not<T extends boolean> = true extends T ? false : true
type NotAnyNever<T> = false extends IsAny<T> ? Not<IsNever<T>> : false

type IsUnknown<T> = unknown extends T ? Not<IsAny<T>> : false

// 使用元组，避免触发分布式条件推断: 因为分布式条件推断会返回类型的联合即 boolean
type IsVoid<T> = [T, IsUndefined<T>] extends [void, false] ? NotAnyNever<T> : false

// Primitive types
type IsUndefined<T> = [T] extends [undefined] ? NotAnyNever<T> : false

type IsNull<T> = [T, NotAnyNever<T>] extends [null, true] ? true : false
type x1 = IsNull<null> // true
type x2 = IsNull<unknown> // false

type IsSymbol<T> = [T, NotAnyNever<T>] extends [symbol, true] ? true : false 

// string, number, bigint, boolean 等也是类似的写法

type IsLiteral<T, U extends LiteralType = LiteralType> = [T] extends [U]
// 字面量值可以赋值给 字面量类型
// 但字面量类型不可以赋值给 字面量值
// 所以反过来 U extends T U是联合类型，触发分布式条件判断
// 只要有一个能赋值给T，则说明T不是字面量值，返回true
// 如果都不能赋值给T，则为never，IsNever就返回true
    ? IsNever<U extends T ? false: never>
    : false

type IsInterface<T> = 
    [T] extends [object]
        ? NotAnyNever<T>
        : false // object, interface, class, function/Function List(Array/Tuple), more

        // 模版字符串字面量检查
type IsTemplateLiteral<T> = T


type Tx<T> = T extends never ? true : false
type Tx2 = Tx<any> // any 赋值给 never 将获得 boolean

// =============================================================================

// 这个判断是普遍的解法，但会污染泛型参数，比如这里多了一个U，会让用的人迷惑
type IsUnion_0<T, U=T> = 
    T extends T ? 
        [U] extends [T] 
            ? false
            : true
    : never


type IsUnion_3<T> = T[] extends (infer U)[] 
    ? T extends infer t 
        ? ([U] extends [t] ? false : true) : never
    : never

type IsUnion<T> = IsUnion_3<T>
type c = IsUnion<'a' | 'b'> // true
type d = IsUnion<'a'> // false
type e = IsUnion<never> // never
// NOTE: 这是一个特例(any 在inter t时不会触发False分支)
// any 在 extends 左侧，右侧是 infer t 时是不会触发分布式条件判断的
// 它不会触发false分支，只会触发true分支(IsUnion_3 true分支返回false)
type T2 = IsUnion<any> // false 

// - 就目前来说，我们讨论的泛型有两个大类: 声明的，求值的(泛型工具)
// 1) 利用泛型的反向推断
// =============================================================================
type IsArray1<T> = [T] extends [unknown[]] ? true : false // BAD CASE, any和never没检查 tuple也没检查
type IsArray<T> = IsArray1<T>
type T9<T> = T extends Array<infer A> ? A : never
type T91 = T9<string[]>
type T10<T> = T extends (infer A)[] ? A : never
type T101 = T9<[string, number]>

// =============================================================================
type IsArray2<T> = 
    NotAnyNever<T> extends true 
        ? [T] extends [any[]] ? Not<IsTuple<T>> : never
        : false
type X = IsArray2<string[]>

// 这是早期检查是不是元组的一种写法，通过 'length' 属性判断
// 数组返回的是 number 类型，元组返回的是 数字字面量如 1,2,3等
// 但后面由于 要支持 [number, ...string, boolean] 这种写法，因此元组也是不定长的
// 元组 ’length‘ 返回的也是 number 类型, 就无法判断了
type IsTuple1<T> = [T] extends [ReadonlyArray<any>] ? number extends T['length'] ? false : true : false
type IsTuple<T> = IsTuple1<T>
type Func = (a: string, b: boolean, ...args: any[]) => void 
type FuncArgs = Parameters<Func>
type T = IsTuple1<FuncArgs> // false 元组检查错误, 应该是元组
type T1 = IsTuple2<FuncArgs> // true 元组检查正确
type T11 = IsTuple2<number[]> // false 元组检查正确

// 唯一有效检查元组的方法
// @see https://github.com/gvergnaud/ts-pattern
type IsTuple2<T> = T extends 
    | readonly []
    | readonly [any, ...any]
    | readonly [...any,any]
    ? true
    : false

// 判断数组/元组是不是有限的
// @see https://github.com/lifaon74/observables
// @see https://stackoverflow.com/a/75944995
type Length<T extends any[]> = T['length'];
type L1 = Length<[string]>                          // 1
type L2 = Length<[]>                                // 0
type L3 = Length<string[]>                          // number
type L4 = Length<[arg1: string, ...args: string[]]> // number
type L5 = Length<[string, ...[string, number], ...string[], string]> // number
type L6 = Length<[string, string?]> //  1 | 2

// 判断数组/元组是不是有限长度的
// 元组如果是无限长度的，那么 ’length' 也是和数组一样是 number 类型，而不是字面里类型
type IsFinite<T extends any[]> =
    number extends T['length'] ? true : false

type Foo1 = IsFinite<[string]>                          // true
type Foo2 = IsFinite<[]>                                // true
type Foo3 = IsFinite<string[]>                          // false
type Foo4 = IsFinite<[arg1: string, ...args: string[]]> // false
type Foo5 = IsFinite<[string, ...[string, number], ...string[], string]> // false
type Foo6 = IsFinite<[string, string?]> //  true

// 判断是不是一个记录
// IsRecord_1 是BAD CASE
type IsRecord_1<T, base = any> = 
    T extends Record<infer Keys, infer X> 
        ? X extends base 
            ? true : false 
        : false 
type R = Record<'a' | 'b' | 'o', 1>
type RT = IsRecord_1<R, number>
// NOTE: 注意any的特例
// boolean 因为推断中Keys没有用到，所以导致产生boolean 
type RT1 = IsRecord_1<any> 
// NOTE: 注意{}的特例
// never 空白对象没有key和value，infer 出来的就是裸类型参数never 
// 又因为裸类型参数never在extends的左侧，所以它直接就返回了never(never在extends的左侧会直接返回never)
type RT2 = IsRecord_1<{}> 

type IsRecord_2<T, base = any> = 
    [T] extends [Record<infer Keys, infer X>] // 加[] 解决T是any的问题
        ? [X] extends [base] // 加[] 解决X是never的问题, 将never变成非裸类型参数, 即T是{}的问题
            ? true : false 
        : false 

type RT11 = IsRecord_2<any> // true
type RT21 = IsRecord_2<{}>  // true


// 取Record的一部分
type FromRecord<T, of extends 'base'|'keys'> = 
    [T] extends [Record<infer Keys, infer Base>]
        ? of extends 'base' 
            ? Base
            : Keys // {base: Base, keys: Keys}[of]
        : never
type RT3 = FromRecord<R, 'keys'> 
type RT4 = FromRecord<R, 'base'> 
type RT5 = [FromRecord<any, 'keys'>, FromRecord<any, 'base'>]
type RT6 = [FromRecord<never, 'keys'>, FromRecord<never, 'base'>] 
type RT7 = [FromRecord<{}, 'keys'>, FromRecord<{}, 'base'>] // [never, never] 正确, 

type A = any extends 1 ? true : false // any 在extends左侧会触发分布式条件判断，且any可以赋值给任何类型所以就返回了 boolean
type B = any extends 1 ? 'a' : 2 // any 在extends左侧会触发分布式条件判断，且any可以赋值给任何类型所以就返回了 2 | "a"
type C = [any] extends [1] ? 'a' : 2 // [any]就不是裸类型any了，也就不会触发分布式条件判断，返回 "a"

// 2) 函数作为结构类型的处理
type IsCallable<T> = T extends (...args: any) => any ? NotAnyNever<T> : false

type IsConstructor<T> = T extends abstract new (...args: any) => any ? NotAnyNever<T> : false

// =============================================================================
// - 利用特殊类型处理来实现”类型的绝对等值检测“
// =============================================================================
type IsRecord2<T, base=any> = T extends Record<infer Keys, infer X> ? Equal2<X, base> : false 

// BASE CASE 1 - any/never/unknown参与的运算会带来难以预知的结果(下面结果都是错的)
type Equal<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false
type D = Equal<any, 1> // true
type E = Equal<[any], [number]> // true
type F = Equal<[any, number], [number, any]> // true

// BAS CASE 2 - (惰性求值的)表达式类型与其结果类型会不等
//  - typeof x
//  - 某些交叉
class MyClass {}
type X1 = typeof MyClass // typeof 是惰性求值的
type X2 = (new () => MyClass) & { // & 是交叉类型 也是惰性求值的
    readonly prototype: MyClass
}
type X3 = {
    new (): MyClass
    readonly prototype: MyClass
}
interface X4  {
    new (): MyClass
    readonly prototype: MyClass
}

// @see https://github.com/microsoft/TypeScript
//  - The keywords `Two conditional types` in src/compiler/checker.ts
type Equal2<A, B> = 
// 泛型参数T是兼容类型
// 并且true分支和false分支也是兼容类型
// 只要这三个都是兼容类型，只要A和B完全相等，
// 则这两个条件表达式(即: (<T>() => T extends A ? 1: 2) 和 (<T>() => T extends B ? 1: 2))才是兼容类型
// 注意这里是使用extends表达式求值, <T>() => T 是惰性求值(它求不出值)
// 所以(<T>() => T extends A ? 1: 2) 和 (<T>() => T extends B ? 1: 2)两个表达式都没有求出值，
// 它们就以表达式类型进行计算，就得到了双表达式的运算
// 但这有个限制，只能比较全等，不能比较传进来的类型较表达式(惰性的，没进行计算的, 还没求出结果的)
    (<T>() => T extends A ? 1: 2) extends
    (<T>() => T extends B ? 1: 2)
    ? true
    : false
    // X3和X4是结构化的比较，是可以直接比较全等的
type T3 = Equal2<X3, X4> // true
type D1 = Equal2<any, 1> // false
type E1 = Equal2<[any], [number]> // false
type F1 = Equal2<[any, number], [number, any]> // false

type T4 = Equal2<X1, X3> // false, 因为X1还没求值
type T5 = Equal2<X1, X4> // false, 因为X1还没求值
type T6 = Equal2<X1, X2> // false, 因为X1还没求值